package com.leixin.utils.generator;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.converts.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @Description: TODO
 * @Company : TODO
 * @Date : 2020-05-02.
 * @Author : LeiXin
 */
@Slf4j
public class MybatisPlusGenerator {

    /**
     * <b>自动生成代码</b>
     *
     * <pre>
     * Generator generator = new Generator()
     *      .setModuleName("test")
     *      .setPackageName("com.rk.dispatch")
     *      .setSwagger2(true) // 是否加入 swagger
     *      .setTestFlag(true) // 生成测试类
     *      //.setCicd(true)// 生成 cicd 文件
     *      .setMavenPom(true)// 生成 pom
     *      .setSpringboot(true)// 生成 springboot xxxApplication.java/application.yml
     *      .setNacosPath("127.0.0.1:8848")// nacos 地址
     *      .setDataSource(new Generator.GeneratorDataSource()
     *          .setDbType(DbType.MYSQL)
     *          .setIp("127.0.0.1")
     *          .setPort("3306")
     *          .setDatabase("test")
     *          .setUserName("root")
     *          .setPassword("123456"))
     *      //.setTableFill(true) // 是否加入公共字段（create_time/update_time/create_id/create_name/update_id/update_name/remark）
     *      .setAuthor("Leixin")
     *      .setTableNames(new String[]{"table1", "table2"});
     * MybatisPlusGenerator.init(generator);
     * </pre>
     *
     * @param {@link Generator}
     * @date 2020-05-02
     * @author LeiXin
     */
    public static void init(Generator generator) {
        /**
         * 代码生成器
         */
        AutoGenerator autoGenerator = new AutoGenerator();

        String str = scanner("是否覆盖源文件（y/yes、n/no）");
        if (str.equals("yes") || str.equals("y")) {
            generator.setFileOverride(true);
        } else if (str.equals("n") || str.equals("no")) {
            generator.setFileOverride(false);
        } else {
            throw new MybatisPlusException("请输入（y/yes、n/no）");
        }

        /**
         * 全局配置
         */
        GlobalConfig globalConfig = new GlobalConfig();
        //生成文件的输出目录
        String projectPath = System.getProperty("user.dir");
        globalConfig.setOutputDir(projectPath + "/src/main/java");
        //Author设置作者
        globalConfig.setAuthor(generator.getAuthor());
        //是否覆盖文件
        globalConfig.setFileOverride(generator.isFileOverride());
        //生成后打开文件
        globalConfig.setOpen(false);
        globalConfig.setSwagger2(generator.isSwagger2());
        // 指定生成日期类型
        globalConfig.setDateType(DateType.TIME_PACK);
        autoGenerator.setGlobalConfig(globalConfig);

        /**
         * 数据源配置
         */
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        // 数据库类型,默认MYSQL
        String dbDriverName = null;
        String dbUrl = null;
        //自定义数据类型转换
        switch (generator.getDataSource().getDbType()) {
            case MYSQL:
                dataSourceConfig.setTypeConvert(new MySqlTypeConvert());
                dbDriverName = com.mysql.cj.jdbc.Driver.class.getName();
//                dbDriverName = "com.mysql.cj.jdbc.Driver";
                dbUrl = "jdbc:mysql://" + generator.getDataSource().getIp() + ":" + generator.getDataSource().getPort() + "/" + generator.getDataSource().getDatabase() + "?serverTimezone=Asia/Shanghai&characterEncoding=utf-8";
                break;
            case POSTGRE_SQL:
                dataSourceConfig.setTypeConvert(new PostgreSqlTypeConvert());
//                dbDriverName = "org.postgresql.Driver";
                dbDriverName = org.postgresql.Driver.class.getName();
                dbUrl = "jdbc:postgresql://" + generator.getDataSource().getIp() + ":" + generator.getDataSource().getPort() + "/" + generator.getDataSource().getDatabase() + "?serverTimezone=Asia/Shanghai&characterEncoding=utf-8";
                break;
            case SQL_SERVER:
                dataSourceConfig.setTypeConvert(new SqlServerTypeConvert());
//                dbDriverName = "net.sourceforge.jtds.jdbc.Driver";
//                dbDriverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
                dbDriverName = com.microsoft.sqlserver.jdbc.SQLServerDriver.class.getName();
                dbUrl = "jdbc:sqlserver://" + generator.getDataSource().getIp() + ":" + generator.getDataSource().getPort() + ";databasename=" + generator.getDataSource().getDatabase();
                break;
            case SQLITE:
                dataSourceConfig.setTypeConvert(new SqliteTypeConvert());
//                dbDriverName = JDBC.class.getName();
//                dbDriverName = "org.sqlite.JDBC";
                dbDriverName = org.sqlite.JDBC.class.getName();
                dbUrl = "jdbc:sqlite:" + generator.getDataSource().getSqliteFileUrl();
                break;
            case ORACLE:
                dataSourceConfig.setTypeConvert(new OracleTypeConvert());
                dbDriverName = "oracle.jdbc.driver.OracleDriver";
//                dbDriverName = oracle.jdbc.driver.OracleDriver.class.getName();
                dbUrl = "jdbc:oracle:thin:@" + generator.getDataSource().getIp() + ":" + generator.getDataSource().getPort() + ":ORCL";
                break;
            default:
                throw new RuntimeException("数据库类型不支持");
        }
        dataSourceConfig.setDbType(generator.getDataSource().getDbType());
        dataSourceConfig.setUrl(dbUrl);
        dataSourceConfig.setDriverName(dbDriverName);
        dataSourceConfig.setUsername(generator.getDataSource().getUserName());
        dataSourceConfig.setPassword(generator.getDataSource().getPassword());
        autoGenerator.setDataSource(dataSourceConfig);

        /**
         * 包配置
         */
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(generator.getModuleName());
        //父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名
        pc.setParent(generator.getPackageName());
        autoGenerator.setPackageInfo(pc);

        /**
         * 项目名称
         */
        String productName = generator.getPackageName().substring(generator.getPackageName().lastIndexOf(".") + 1);
        String projectName = productName + "-" + generator.getModuleName();

        /**
         * 自定义配置
         */
        String finalDriverName = dbDriverName;
        String finalDbUrl = dbUrl;
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                //在.ftl(或者是.vm)模板中，通过${cfg.abc}获取属性
                Map<String, Object> map = new HashMap<>();

                map.put("dbType", generator.getDataSource().getDbType().getDb());
                map.put("dbIp", generator.getDataSource().getIp());
                map.put("dbPort", generator.getDataSource().getPort());
                map.put("dbUsername", generator.getDataSource().getUserName());
                map.put("dbPwd", generator.getDataSource().getPassword());
                map.put("dbDatabase", generator.getDataSource().getDatabase());
                map.put("dbDriverName", finalDriverName);
                map.put("dbUrl", finalDbUrl);
                map.put("sqliteFileUrl", generator.getDataSource().getSqliteFileUrl());

                map.put("projectName", projectName);
                map.put("productName", productName);
                map.put("packageName", generator.getPackageName());
                map.put("chainModel", true);
                map.put("xls", generator.isXls());
                map.put("valid", generator.isValidated());
                map.put("baseColumnList", true);
                map.put("moduleName", pc.getModuleName());
                map.put("upperModule", firstStrUpper(pc.getModuleName()));
                map.put("applicationName", firstStrUpper(pc.getModuleName()) + "Application");
                map.put("nacosPath", generator.getNacosPath());
                if (generator.getNacosPath().contains(".")) {
                    map.put("cicdNacosPath", generator.getNacosPath().substring(0, generator.getNacosPath().indexOf(".")));
                } else {
                    map.put("cicdNacosPath", generator.getNacosPath());
                }
                this.setMap(map);
            }
        };

        /**
         * 模板
         */
        //如果模板引擎是 freemarker
        String entityTempPath = "/templates/entity.java.ftl";
        String mapperxmlTemplatePath = "/templates/mapper.xml.ftl";

        // 如果模板引擎是 velocity
//        String templatePath = "D:/MyWork/Project/rockontrol/city/city-material/src/main/resources/templates/mapper.xml.vm";

        /**
         * 自定义输出配置
         */
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(mapperxmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return projectPath + "/src/main/resources/config/mybatis/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "-mapper" + StringPool.DOT_XML;
            }
        });

        if (generator.isPojoSeparate()) {

            if (generator.getPojoProjectName() == null) {
                generator.setPojoProjectName(projectName + "-pojo");
            }

            focList.add(new FileOutConfig(entityTempPath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/" + generator.getPojoProjectName()
                            + "/src/main/java/" + pc.getParent().replace(".", "/")
                            + "/entity/" + tableInfo.getEntityName() + StringPool.DOT_JAVA;
                }
            });
            focList.add(new FileOutConfig("/templates/maven/entity-pom.xml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/" + generator.getPojoProjectName() + "/pom" + StringPool.DOT_XML;
                }
            });
        }

        // Feign
        if (generator.isFeign()) {

            if (generator.getFeignProjectName() == null) {
                generator.setFeignProjectName(projectName + "-api");
            }

            focList.add(new FileOutConfig("/templates/feign/feign.java.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/" + generator.getFeignProjectName()
                            + "/src/main/java/" + pc.getParent().replace(".", "/")
                            + "/feign/" + tableInfo.getEntityName() + "FeignClient" + StringPool.DOT_JAVA;
                }
            });
            focList.add(new FileOutConfig("/templates/feign/fallback.java.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return projectPath + "/" + generator.getFeignProjectName()
                            + "/src/main/java/" + pc.getParent().replace(".", "/")
                            + "/fallback/" + tableInfo.getEntityName() + "Fallback" + StringPool.DOT_JAVA;
                }
            });
            focList.add(new FileOutConfig("/templates/maven/feign-pom.xml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/" + generator.getFeignProjectName() + "/pom" + StringPool.DOT_XML;
                }
            });
        }

        // springboot
        String applicationJavaTempPath = "/templates/sb/application.java.ftl";
        String applicationYmlTempPath = "/templates/sb/application.yml.ftl";
        if (generator.isSpringboot()) {
            focList.add(new FileOutConfig(applicationJavaTempPath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/"
                            + pc.getParent().replace(".", "/")
                            + "/" + firstStrUpper(pc.getModuleName()) + "Application" + StringPool.DOT_JAVA;
                }
            });
            focList.add(new FileOutConfig(applicationYmlTempPath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/resources/config/application.yml";
                }
            });
        }

        // springboot
        String simpleTestTempPath = "/templates/test/simpleTest.java.ftl";
        String systemRunnerTestTempPath = "/templates/test/systemRunnerTest.java.ftl";
        if (generator.isSpringboot()) {
            focList.add(new FileOutConfig(simpleTestTempPath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/test/java/" + pc.getParent().replace(".", "/")
                            + "/" + firstStrUpper(pc.getModuleName()) + "SimpleTest" + StringPool.DOT_JAVA;
                }
            });
            focList.add(new FileOutConfig(systemRunnerTestTempPath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/test/java/" + pc.getParent().replace(".", "/")
                            + "/" + firstStrUpper(pc.getModuleName()) + "SystemRunnerTest" + StringPool.DOT_JAVA;
                }
            });

        }

        // pom.xml
        if (generator.isMavenPom()) {
            focList.add(new FileOutConfig("/templates/maven/pom.xml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/pom.xml";
                }
            });
        }

        // CICD
        // config/default.yml
        // config/master.yml
        // gitlab-ci.yml
        // dockerfile.default.yml
        // helmx.default.yml
        // helmx.project.yml
        if (generator.isCicd()) {
            focList.add(new FileOutConfig("/templates/cicd/default.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/config/default.yml";
                }
            });
            focList.add(new FileOutConfig("/templates/cicd/master.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/config/master.yml";
                }
            });

            focList.add(new FileOutConfig("/templates/cicd/.gitlab-ci.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/.gitlab-ci.yml";
                }
            });
            focList.add(new FileOutConfig("/templates/cicd/dockerfile.default.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/dockerfile.default.yml";
                }
            });
            focList.add(new FileOutConfig("/templates/cicd/helmx.default.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/helmx.default.yml";
                }
            });
            focList.add(new FileOutConfig("/templates/cicd/helmx.project.yml.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/helmx.project.yml";
                }
            });
            focList.add(new FileOutConfig("/templates/git/gitignore.ftl") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/.gitignore";
                }
            });
        }
        cfg.setFileOutConfigList(focList);
        autoGenerator.setCfg(cfg);

        /**
         * 配置模板
         */
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
//        templateConfig.setEntity("templates/entity.java.vm");
//        templateConfig.setController("templates/controller.java.vm");
//        templateConfig.setServiceImpl("templates/serviceImpl.java.vm");
//        templateConfig.setMapper("templates/mapperImpl.xml.vm");
//        templateConfig.setXml("templates/mapper.xml.vm");

//        templateConfig.setEntity("templates/entity.java.ftl");
//        templateConfig.setController("templates/controller.java.ftl");
//        templateConfig.setServiceImpl("templates/serviceImpl.java.ftl");
//        templateConfig.setMapper("templates/mapperImpl.xml.ftl");
//        templateConfig.setXml("templates/mapper.xml.ftl");

        if (generator.isPojoSeparate()) {
            if (generator.getPojoProjectName() == null) {
                throw new MybatisPlusException("请输入 Pojo 工程名称");
            }
            templateConfig.setEntity(null);
        } else {
            templateConfig.setEntity("templates/entity.java");
        }
        templateConfig.setController("templates/controller.java");
        templateConfig.setServiceImpl(null);
        templateConfig.setService("templates/service.java");
        templateConfig.setMapper("templates/mapper.java");
        templateConfig.setXml(null);
        autoGenerator.setTemplate(templateConfig);



        /*
         * 策略配置
         */
        StrategyConfig strategy = new StrategyConfig();
        //设置命名格式
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setInclude(generator.getTableNames());
        strategy.setTablePrefix(generator.getTablePrefix());
        strategy.setFieldPrefix(generator.getTableFieldPrefix());
        strategy.setEntityTableFieldAnnotationEnable(true);
        //实体是否为lombok模型（默认 false）
        strategy.setEntityLombokModel(generator.isEntityLombokModel());
        //生成 @RestController 控制器
        strategy.setRestControllerStyle(true);
//        strategy.setSuperEntityClass(Model.class);
//        strategy.setSuperMapperClass(BaseMapper.class.getName());
//        strategy.setSuperServiceClass(ServiceImpl.class.getName());
//        strategy.setSuperControllerClass(BaseController.class.getName());

        //设置自定义基础的Entity类，公共字段
//        strategy.setSuperEntityColumns("id");
        //驼峰转连字符
        strategy.setControllerMappingHyphenStyle(true);
        //表名前缀
        strategy.setTablePrefix(pc.getModuleName() + "_");

        if (generator.isTableFill()) {
            List<TableFill> list = new ArrayList<>();
            list.add(new TableFill("createTime", FieldFill.INSERT));
            list.add(new TableFill("updateTime", FieldFill.INSERT_UPDATE));
            list.add(new TableFill("createId", FieldFill.DEFAULT));
            list.add(new TableFill("createName", FieldFill.DEFAULT));
            list.add(new TableFill("updateId", FieldFill.INSERT_UPDATE));
            list.add(new TableFill("updateName", FieldFill.INSERT_UPDATE));
            list.add(new TableFill("remark", FieldFill.DEFAULT));
            strategy.setTableFillList(list);
        }

        autoGenerator.setStrategy(strategy);
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());

        ConfigBuilder config = new ConfigBuilder(autoGenerator.getPackageInfo(), autoGenerator.getDataSource(), strategy, templateConfig, globalConfig);
        List<TableInfo> tableInfoList = config.getTableInfoList();
        if (tableInfoList.size() > 0) {
            for (TableInfo tableInfo : tableInfoList) {
                System.out.println(tableInfo.getName());
            }
        }
        autoGenerator.execute();

    }

    public static String firstStrUpper(String moduleName) {
        String first = moduleName.substring(0, 1);
        String after = moduleName.substring(1); //substring(1),获取索引位置1后面所有剩余的字符串
        first = first.toUpperCase();
        after = after.toLowerCase();
        return first + after;
    }

    public static String firstStrLower(String str) {
        String first = str.substring(0, 1).toLowerCase();
        String after = str.substring(1); //substring(1),获取索引位置1后面所有剩余的字符串
        return first + after;
    }

    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append(tip + "：");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "！");
    }

}
